package ch.uzh.ifi.seal.ssqa.importer.code;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IInclude;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;

import ch.uzh.ifi.seal.ssqa.model.CClass;
import ch.uzh.ifi.seal.ssqa.model.ModelFactory;
import ch.uzh.ifi.seal.ssqa.model.Source;

public class SourceCodeAnalyser {

	// file under analysis
	private IFile file;
	private ITranslationUnit tu;
	private IASTTranslationUnit ast;

	/**
	 * Constructor, that takes the file to analyze as a parameter
	 * 
	 * @param file
	 */
	public SourceCodeAnalyser(IFile file) {
		this.setPath(file);
	}

	private void setPath(IFile file) {

		try {
			if (file != null) {
				this.tu = (ITranslationUnit) CoreModel.getDefault()
						.create(file);
				this.ast = this.tu.getAST();
				System.out.println("[Source Code Analyser]: GET AST of "
						+ file.getName());
			} else {
				System.out.println("[Source Code Analyser]: File is null");
			}
		} catch (CoreException e) {
			this.ast = null;
			if (file != null) {
				System.out.println("[Source Code Analyser]: File is "
						+ file.getName());
			}
			e.printStackTrace();
		}
		this.file = file;
	}

	/**
	 * creates a Source of the EMF model from the source file provided
	 * Initializes the visitor and calls methods that take some information
	 * directly form the tu.
	 * 
	 * @return
	 */
	public Source getSource() {
		SourceCodeVisitor visitor = new SourceCodeVisitor();
		if (this.ast != null) {
			this.ast.accept(visitor);
		}
		Source source = ModelFactory.eINSTANCE.createSource();
		if (this.file != null) {
			source.setName(this.file.getName());
			source.setLineCount(new LinesOfCodeCounter(this.file)
					.getCodeLines());
			source.setFilePath(this.file.getLocation().toString());
			source.setProjectPath(this.tu.getPath().toString());
			source.getIncludeNames().addAll(this.getIncludes());
			source.getFunctionNames().addAll(this.getFunctions());
			source.getClasses().addAll(
					(Collection<? extends CClass>) visitor.getClasses());
			source.setCyclomaticComplexity(visitor.getComplexity());
			source.setHeaderFile(this.tu.isHeaderUnit());
		}
		return source;
	}

	private List<String> getIncludes() {
		IInclude[] includes;
		ArrayList<String> includeList = new ArrayList<String>();
		try {
			includes = this.tu.getIncludes();
			for (IInclude include : includes) {
				if (!include.getElementName().isEmpty()) {
					includeList.add(include.getElementName());
				}
			}
		} catch (CModelException e) {
			e.printStackTrace();
		}
		return includeList;
	}

	private List<String> getFunctions() {
		List<String> functions = new ArrayList<String>();
		try {
			List<ICElement> elements = this.tu
					.getChildrenOfType(ICElement.C_FUNCTION);
			for (ICElement element : elements) {
				functions.add(element.getElementName());
			}
		} catch (CModelException e) {
			e.printStackTrace();
		}
		return functions;
	}
}
